﻿using System.Reflection;
using TianYu.Core.Common;

namespace System
{
    public static class TypeExtension
    {
        /// <summary>在所有程序集中查找指定基类或接口的子类实现</summary>
        /// <param name="@this">基类或接口</param>
        /// <returns></returns>
        public static IEnumerable<Type> GetAllSubclasses(this Type @this)
        {
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in @this.GetSubclasses(asm))
                {
                    yield return type;
                }
            }
        }

        /// <summary>在指定程序集中查找指定基类的子类</summary>
        /// <param name="@this">基类或接口</param>
        /// <param name="assembly">指定程序集</param>
        /// <returns></returns>
        public static IEnumerable<Type> GetSubclasses(this Type @this, Assembly assembly)
        {
            Check.NotNull(assembly, nameof(assembly));
            Type[] ts;
            try
            {
                ts = assembly.GetTypes();
            }
            catch
            {
                yield break;
            }

            foreach (var item in ts)
            {
                if (item.IsInterface || item.IsAbstract || item.IsGenericType) continue;
                if (@this != item && @this.IsAssignableFrom(item))
                    yield return item;
            }
        }

        /// <summary>反射创建指定类型的实例</summary>
        /// <param name="type">类型</param>
        /// <param name="parameters">参数数组</param>
        /// <returns></returns>
        public static Object CreateInstance(this Type @this, params Object[] parameters)
        {
            if (parameters == null || parameters.Length == 0)
            {
                // 基元类型
                return Type.GetTypeCode(@this) switch
                {
                    TypeCode.Empty or TypeCode.DBNull => null,
                    TypeCode.Boolean => false,
                    TypeCode.Char => '\0',
                    TypeCode.SByte => (SByte)0,
                    TypeCode.Byte => (Byte)0,
                    TypeCode.Int16 => (Int16)0,
                    TypeCode.UInt16 => (UInt16)0,
                    TypeCode.Int32 => 0,
                    TypeCode.UInt32 => 0U,
                    TypeCode.Int64 => 0L,
                    TypeCode.UInt64 => 0UL,
                    TypeCode.Single => 0F,
                    TypeCode.Double => 0D,
                    TypeCode.Decimal => 0M,
                    TypeCode.DateTime => DateTime.MinValue,
                    TypeCode.String => String.Empty,
                    _ => Activator.CreateInstance(@this, true),
                };
            }
            else
            {
                return Activator.CreateInstance(@this, parameters);
            }
        }

        public static List<PropertyInfo> GetProperties(this Type @this, List<string> propertyNames)
        {
            if (propertyNames == null || propertyNames.Count < 1) return new List<PropertyInfo>();

            return @this.GetProperties()
                   .ToList()
                   .Where(a => propertyNames.Contains(a.Name, StringComparer.OrdinalIgnoreCase))
                   .Select(a => a)
                   .ToList();
        }
    }
}
